package com.aplink.generic.widgets.util;

import java.lang.ref.WeakReference;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LongSparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;

import com.aplink.generic.adapter.ConfigAdapter;
import com.aplink.generic.pojo.GenericObject;

public class SwipeItemDeletion implements OnTouchListener {
	private static final int MOVE_DURATION = 150;
	private static final int SWIPE_DURATION = 250;
	private ConfigAdapter mConfigAdapter;
	private Context mContext;
	private float mDownX;
	private LongSparseArray<Integer> mItemIdTopMap;
	boolean mItemPressed = false;
	private int mSwipeSlop = -1;
	boolean mSwiping = false;
	private AbsListView mViewGroup;
	private View mChildView;
	public static int CANCEL = -1;
	public static int REMOVE = 1;
	private boolean isRemove;

	public static class HandleSwipeItemDeletion extends Handler {
		private WeakReference<SwipeItemDeletion> instance;

		public HandleSwipeItemDeletion(SwipeItemDeletion swipeItemDeletion) {
			instance = new WeakReference<SwipeItemDeletion>(swipeItemDeletion);
		}

		@Override
		public void handleMessage(Message msg) {
			if (msg.what == CANCEL) {
				instance.get().isRemove = false;
				instance.get().mChildView.animate().setDuration(MOVE_DURATION)
						.alpha(1).translationX(0);
			} else if (msg.what == REMOVE) {
				instance.get().removeView();
			}
		}
	}

	public SwipeItemDeletion(Context context, AbsListView viewGroup) {
		mContext = context;
		mViewGroup = viewGroup;
		mItemIdTopMap = new LongSparseArray<>();
	}

	private void animateRemoval() {
		int firstVisiblePosition = mViewGroup.getFirstVisiblePosition();
		for (int i = 0; i < mViewGroup.getChildCount(); ++i) {
			View child = mViewGroup.getChildAt(i);
			if (child != mChildView) {
				int position = firstVisiblePosition + i;
				long itemId = mConfigAdapter.getItemId(position);
				mItemIdTopMap.put(itemId, child.getTop());
			}
		}
		mConfigAdapter.remove((GenericObject) mConfigAdapter.getItem(mViewGroup
				.getPositionForView(mChildView)));

	}

	private void removeView() {
		final ViewTreeObserver observer = mViewGroup.getViewTreeObserver();
		observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
			@Override
			public boolean onPreDraw() {
				observer.removeOnPreDrawListener(this);
				boolean firstAnimation = true;
				int firstVisiblePosition = mViewGroup.getFirstVisiblePosition();
				for (int i = 0; i < mViewGroup.getChildCount(); ++i) {
					final View child = mViewGroup.getChildAt(i);
					int position = firstVisiblePosition + i;
					long itemId = mConfigAdapter.getItemId(position);
					Integer startTop = mItemIdTopMap.get(itemId);
					int top = child.getTop();
					if (startTop != null) {
						if (startTop != top) {
							int delta = startTop - top;
							child.setTranslationY(delta);
							child.animate().setDuration(MOVE_DURATION)
									.translationY(0);
							if (firstAnimation) {
								child.animate().setListener(
										new AnimatorListener() {

											@Override
											public void onAnimationCancel(
													Animator animation) {
											}

											@Override
											public void onAnimationEnd(
													Animator animation) {
												mSwiping = false;
												mViewGroup.setEnabled(true);
											}

											@Override
											public void onAnimationRepeat(
													Animator animation) {
											}

											@Override
											public void onAnimationStart(
													Animator animation) {

											}
										});
								firstAnimation = false;
							}
						}
					} else {
						int childHeight = child.getHeight();
						startTop = top + (i > 0 ? childHeight : -childHeight);
						int delta = startTop - top;
						child.setTranslationY(delta);
						child.animate().setDuration(MOVE_DURATION)
								.translationY(0);
						if (firstAnimation) {
							child.animate().setListener(new AnimatorListener() {

								@Override
								public void onAnimationCancel(Animator animation) {

								}

								@Override
								public void onAnimationEnd(Animator animation) {
									mSwiping = false;
									mViewGroup.setEnabled(true);
								}

								@Override
								public void onAnimationRepeat(Animator animation) {

								}

								@Override
								public void onAnimationStart(Animator animation) {

								}
							});
							firstAnimation = false;
						}
					}
				}
				mItemIdTopMap.clear();
				return true;
			}
		});
	}

	public ConfigAdapter getConfigAdapter() {
		return mConfigAdapter;
	}

	@Override
	public boolean onTouch(final View childView, MotionEvent event) {
		mChildView = childView;
		if (mSwipeSlop < 0) {
			mSwipeSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
		}
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (mItemPressed) {
				return false;
			}
			mItemPressed = true;
			mDownX = event.getX();
			mChildView.setPressed(true);
			break;
		case MotionEvent.ACTION_CANCEL:
			mChildView.setAlpha(1);
			mChildView.setTranslationX(0);
			mItemPressed = false;
			break;
		case MotionEvent.ACTION_MOVE: {
			float x = event.getX() + mChildView.getTranslationX();
			float deltaX = x - mDownX;
			float deltaXAbs = Math.abs(deltaX);
			if (!mSwiping) {
				if (deltaXAbs > mSwipeSlop) {
					mSwiping = true;
					mViewGroup.requestDisallowInterceptTouchEvent(true);
				}
			}
			if (mSwiping) {
				mChildView.setTranslationX((x - mDownX));
				mChildView.setAlpha(1 - deltaXAbs / mViewGroup.getWidth());
			}
		}
			break;
		case MotionEvent.ACTION_UP: {
			mChildView.setPressed(false);
			if (mSwiping) {
				float x = event.getX() + mChildView.getTranslationX();
				float deltaX = x - mDownX;
				float deltaXAbs = Math.abs(deltaX);
				float fractionCovered;
				float endX;
				float endAlpha;
				if (deltaXAbs > mViewGroup.getWidth() / 4) {
					fractionCovered = deltaXAbs / mViewGroup.getWidth();
					endX = deltaX < 0 ? -mViewGroup.getWidth() : mViewGroup
							.getWidth();
					endAlpha = 0;
					isRemove = true;
				} else {
					fractionCovered = 1 - (deltaXAbs / mViewGroup.getWidth());
					endX = 0;
					endAlpha = 1;
					isRemove = false;
				}
				long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);
				mViewGroup.setEnabled(false);
				mChildView.animate().setDuration(duration).alpha(endAlpha)
						.translationX(endX).setListener(new AnimatorListener() {

							@Override
							public void onAnimationCancel(Animator animation) {

							}

							@Override
							public void onAnimationEnd(Animator animation) {
								mViewGroup.setAlpha(1);
								mViewGroup.setTranslationX(0);
								if (isRemove) {
									animateRemoval();
								} else {
									mSwiping = false;
									mViewGroup.setEnabled(true);
								}
							}

							@Override
							public void onAnimationRepeat(Animator animation) {

							}

							@Override
							public void onAnimationStart(Animator animation) {

							}
						});
			} else {
				int position = mViewGroup.getPositionForView(mChildView);
				if (position != AdapterView.INVALID_POSITION) {
					mViewGroup.performItemClick(
							mViewGroup.getChildAt(position
									- mViewGroup.getFirstVisiblePosition()),
							position, mViewGroup.getItemIdAtPosition(position));
				}
			}
			mItemPressed = false;
			break;
		}
		}
		return true;
	}

	public void setConfigAdapter(ConfigAdapter configAdapter) {
		mConfigAdapter = configAdapter;
		mConfigAdapter.setHandleSwipeItemDeletion(new HandleSwipeItemDeletion(
				this));
	}
}
